﻿using ICSharpCode.AvalonEdit;
using JVMTI_CONN_CONNECT;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Shell;

namespace JVMTI_CORE_SHELL
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private JvmtiEvents dataSources;
        private LifeCycleViewModels threadLifeCycleViewModels;
        private LifeCycleViewModels resourceLifeCycleViewModels;
        public MainWindow()
        {
            var Resizable_BorderLess_Chrome = new WindowChrome();
            Resizable_BorderLess_Chrome.GlassFrameThickness = new Thickness(0);
            Resizable_BorderLess_Chrome.CornerRadius = new CornerRadius(0);
            Resizable_BorderLess_Chrome.CaptionHeight = 5.0;
            WindowChrome.SetWindowChrome(this, Resizable_BorderLess_Chrome);
            InitializeComponent();
            this.MouseLeftButtonDown += MainWindow_MouseLeftButtonDown;
            this.Loaded += MainWindow_Loaded;
            this.SizeChanged += MainWindow_SizeChanged;
            App.Current.MainWindow = this;
        }

        void MainWindow_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            lifeCycleView.InvalidateMeasure();
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {

            transitIn = this.Resources["transitIn"] as Storyboard;
            busy = this.Resources["busy"] as Storyboard;
            idle = this.Resources["idle"] as Storyboard;
            currentInView = threadLifeCycleViewModels;
            calcAndBind(currentInView);
            //resourceView.ItemsSource = resourceLifeCycleViewModels;
            transitIn.Begin(this.Content as Border);
            //lastShown = grid;
            timeSliceInfo.Text = string.Format("showing from {0} to {1}", App.MinimumStartTime, App.MaximumThreadEndTime);

        }
        public MainWindow(JvmtiEvents dataSource)
            : this()
        {
            dataSources = dataSource;
            threadLifeCycleViewModels = dataSources.CreateThreadLifeCycleView();
            resourceLifeCycleViewModels = dataSource.CreateResourceLifeCycleView();
        }

        void timeSlicer_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            double lowerTime = App.MinimumStartTime + (App.MaximumThreadEndTime - App.MinimumStartTime) * timeSlicer.LowerValue / 100;
            double upperTime = App.MinimumStartTime + (App.MaximumThreadEndTime - App.MinimumStartTime) * timeSlicer.UpperValue / 100;
            timeSliceInfo.Text = string.Format("click ok to show time from {0} to {1}", lowerTime, upperTime);
        }

        void MainWindow_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            try
            {
                this.DragMove();
                if (App.CodeViewerWindow != null) App.CodeViewerWindow.Hide();
            }
            catch { }
        }

        Storyboard transitIn;
        Storyboard idle;
        Storyboard busy;
        Grid lastShown;
        Grid nextAnimation;
        private void tabCtrl_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (transitIn == null) return;
            var current = (((sender as TabControl).SelectedItem as TabItem).Content as Grid);
            if (lastShown == current || lastShown == null) return;
            nextAnimation = current;
            transitIn.Begin(current);
            lastShown = current;
        }

        Expander previsoulyExpandedComponent;
        private void Expander_Expanded(object sender, RoutedEventArgs e)
        {
            var expander = sender as Expander;
            DependencyObject parent = VisualTreeHelper.GetParent(expander);
            while (!(parent is ListBoxItem))
                parent = VisualTreeHelper.GetParent(parent);
            var selected = lifeCycleView.SelectedIndex;
            (parent as ListBoxItem).BringIntoView();
            if (previsoulyExpandedComponent != null && previsoulyExpandedComponent != sender)
            {
                previsoulyExpandedComponent.IsExpanded = false;
            }
            previsoulyExpandedComponent = sender as Expander;
        }

        private async void filterByTime(object sender, RoutedEventArgs e)
        {
            if (applyTimeSelection == sender)
            {
                double lowerTime = App.MinimumStartTime + (App.MaximumThreadEndTime - App.MinimumStartTime) * timeSlicer.LowerValue / 100;
                double upperTime = App.MinimumStartTime + (App.MaximumThreadEndTime - App.MinimumStartTime) * timeSlicer.UpperValue / 100;
                //redraw here.
                workerIndicator.Visibility = System.Windows.Visibility.Visible;
                var currModel = (lifeCycleView.ItemsSource as LifeCycleViewModels);
                (sender as Button).IsEnabled = false;
                var newModels = new LifeCycleViewModels();
                await Task.Run(() =>
                    {
                        Thread.Sleep(1000);
                        foreach (var viewModel in currModel)
                        {
                            LifeCycleViewModel model = new LifeCycleViewModel(viewModel.Id, viewModel.KeyEvents.AsParallel<ILifeEvent>().Where<ILifeEvent>(o => o.Timestamp >= lowerTime && o.Timestamp <= upperTime));
                            model.DisplayName = viewModel.DisplayName;
                            newModels.Add(model);
                        }
                    });
                workerIndicator.Visibility = System.Windows.Visibility.Collapsed;
                timeSlicer.LowerValue = 0;
                timeSlicer.UpperValue = 100;
                calcAndBind(newModels);
                (sender as Button).IsEnabled = true;
                timeSliceInfo.Text = string.Format("showing from {0} to {1}", App.MinimumStartTime, App.MaximumThreadEndTime);
            }
            else
            {
                calcAndBind(currentInView);
            }
            timeSliceInfo.Text = string.Format("showing from {0} to {1}", App.MinimumStartTime, App.MaximumThreadEndTime);
            //ThreadPool.QueueUserWorkItem();
        }

        private void calcAndBind(LifeCycleViewModels newModels)
        {
            double maxTime, minTime;
            App.MaximumThreadEndTime = maxTime = newModels.Max<LifeCycleViewModel>(t => t.EndTimeStamp);
            App.MinimumStartTime = minTime = newModels.Min<LifeCycleViewModel>(t => t.StartTimeStamp);
            lifeCycleView.ItemsSource = newModels;
            lifeCycleView.Items.Refresh();
        }
        LifeCycleViewModels currentInView;
        private void switchView(object sender, MouseButtonEventArgs e)
        {
            for (int i = 0; i < viewGroup1Panel.Children.Count; i++)
            {
                (viewGroup1Panel.Children[i] as TextBlock).Foreground = new SolidColorBrush(Colors.Black);
            }
            (sender as TextBlock).Foreground = App.AccentBrush;
            if (sender == _viewGroup1_1)
            {
                calcAndBind(threadLifeCycleViewModels);
                currentInView = threadLifeCycleViewModels;
            }
            if (sender == _viewGroup1_2)
            {
                calcAndBind(resourceLifeCycleViewModels);
                currentInView = resourceLifeCycleViewModels;
            }
        }

        private void lifeCycleView_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {

        }



        private async void Hyperlink_Click(object sender, RoutedEventArgs e)
        {

            CodeViewer cv;
            if (App.CodeViewerWindow != null)
                cv = App.CodeViewerWindow;
            else
                App.CodeViewerWindow = cv = new CodeViewer();
            var stackTraceItem = ((sender as Hyperlink).DataContext as StackTraceItem);
            var fileName = App.searchForFiles(stackTraceItem.FileName);
            var lineno = 1;
            var content = "we cannot find this file in your search path. Add search path by clicking setting.\r\n";
            var extension = ".txt";
            var header = "unable to locate source";
            if (fileName != null)
            {
                lineno = stackTraceItem.LineNumber;
                content = File.ReadAllText(fileName);
                FileInfo fi = new FileInfo(fileName);
                extension = fi.Extension;
                header = fileName;
            }

            var editor = new TextEditor() { Text = content };
            editor.SyntaxHighlighting = ICSharpCode.AvalonEdit.Highlighting.HighlightingManager.Instance.GetDefinitionByExtension(extension);
            //editor.ScrollToLine(lineno);
            if (cv.EditorTabs.Items.Cast<TabItem>().Select<TabItem, string>(tab => (tab.DataContext as StackTraceItem).FileName).Contains(fileName) == false)
                cv.EditorTabs.Items.Add(new TabItem() { Header = header, Content = editor, DataContext = stackTraceItem, FontSize = 14 });
            //editor.Width = editor.Height = 500;
            editor.ShowLineNumbers = true;
            cv.Show();

            var hyperlink = (sender as Hyperlink);
            UIElement container = VisualTreeHelper.GetParent(hyperlink.TemplatedParent) as UIElement;
            var width = System.Windows.SystemParameters.PrimaryScreenWidth;
            var height = System.Windows.SystemParameters.PrimaryScreenHeight;
            var point = this.TranslatePoint(new Point(0, 0), container);
            var realCoord = new Point(this.Left - point.X, this.Top - point.Y);
            if (realCoord.Y >= height / 2)
            {
                cv.Top = 0;
                cv.Height = realCoord.Y - 80;
            }
            else
            {
                cv.Top = realCoord.Y + 40;
                cv.Height = height - realCoord.Y - 80;
            }
            cv.Left = realCoord.X;

            cv.Activate();
            editor.ScrollToLine(lineno);
        }

        private void Grid_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.F && Keyboard.Modifiers == ModifierKeys.Control)
            {
                try
                {
                    var bags = PropertyBags.ConvertToPropertyBags(currentInView.First().KeyEvents[0]);
                    QueryOptions options = new QueryOptions() { StartTime = 0, EndTime = 100, Bags = bags };
                    QueryInterface page = new QueryInterface(options);
                    page.Owner = this;
                    page.Closed += page_Closed;
                    page.Show();
                    busy.Begin();
                }
                catch (NullReferenceException) { }
            }

        }

        void page_Closed(object sender, EventArgs e)
        {
            idle.Begin();
            var page = sender as QueryInterface;
            var inUse = (lifeCycleView.ItemsSource as LifeCycleViewModels);
            if (page.ApplyRequested)
            {
                var option = page.DataContext as QueryOptions;
                double lowerTime = App.MinimumStartTime + (App.MaximumThreadEndTime - App.MinimumStartTime) * option.StartTime / 100;
                double upperTime = App.MinimumStartTime + (App.MaximumThreadEndTime - App.MinimumStartTime) * option.EndTime / 100;
                var events = inUse.SelectMany<LifeCycleViewModel, ILifeEvent>(model => model.KeyEvents).Where<ILifeEvent>(k => k.Timestamp >= lowerTime && k.Timestamp <= upperTime);
                if (events.Count() > 0)
                {
                    var props = events.First().GetType().GetProperties();
                    foreach (var propertyBag in option.Bags)
                    {
                        var currProp = props.FirstOrDefault<PropertyInfo>(prop => prop.Name == propertyBag.PropertyName);
                        events = events.Where<ILifeEvent>(k => propertyBag.Value==null || ((currProp.GetValue(k) == null && propertyBag.Value.ToString().Trim() == string.Empty) || currProp.GetValue(k) != null && currProp.GetValue(k).ToString().Contains(propertyBag.Value.ToString())));
                    }
                    foreach (var evt in events)
                    {
                        evt.HighlightingEvent = true;
                    }
                    calcAndBind(inUse);
                }
            }
            else if (page.ApplyClearSearch)
            {
                var option = page.DataContext as QueryOptions;
                var events = inUse.SelectMany<LifeCycleViewModel, ILifeEvent>(model => model.KeyEvents);
                foreach (var evt in events)
                {
                    evt.HighlightingEvent = false;
                }
                calcAndBind(inUse);
            }
        }

        //Launch contextual search
        private void Hyperlink_Click_1(object sender, RoutedEventArgs e)
        {
            var context = (sender as Hyperlink).DataContext.ToString();
            PropertyBags propBag = new PropertyBags();
            propBag.Add(new PropertyBag() { PropertyName = "Object", Value = context });
            QueryOptions options = new QueryOptions() { StartTime = 0, EndTime = 100, Bags = propBag };
            QueryInterface page = new QueryInterface(options);
            page.Owner = this;
            page.Closed += page_Closed;
            page.Show();
            busy.Begin();
        }

        private void settingPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Settings setting = new Settings(UserPreference.Default);
            setting.Owner = this;
            setting.Show();
            setting.Closed += setting_Closed;
            busy.Begin();
        }

        void setting_Closed(object sender, EventArgs e)
        {
            idle.Begin();
        }

        private void switchButton_Click(object sender, RoutedEventArgs e)
        {
            App.RelaunchWithSelection(this);
        }

        private void Focus(object sender, RoutedEventArgs e)
        {
            var eventSelected = (sender as Button).DataContext as ILifeEvent;
            if (eventSelected == null) return;
            var centered = eventSelected.Timestamp;
            var ratio = (centered - App.MinimumStartTime) / (App.MaximumThreadEndTime - App.MinimumStartTime);
            timeSlicer.LowerValue = ratio * 100;
            //timeSlicer.UpperValue = ratio * 1.1;
        }
    }
}
